#include "console_timer.h"
#include "dht11.h"

#define Delay_ms(ms)   timer_delay_ms(ms)
#define Delay_us(us)   timer_delay_us(us)


/* 传入DHT11结构体指针 可初始化GPIO引脚 */
#define __GPIO_INIT(DHT11)              {   GPIO_InitTypeDef GPIO_InitStructure; \
                                            GPIO_InitStructure.GPIO_Mode        = GPIO_Mode_OUT; \
                                            GPIO_InitStructure.GPIO_PuPd        = GPIO_PuPd_UP; \
                                            GPIO_InitStructure.GPIO_Speed       = GPIO_Speed_50MHz; \
                                            GPIO_InitStructure.GPIO_OType       = GPIO_OType_PP; \
                                            GPIO_InitStructure.GPIO_Pin         = DHT11->gpio_pin; \
                                            GPIO_Init(DHT11->gpio_port,&GPIO_InitStructure);    }

#define __GPIO_CONFIG_OUT_PP(DHT11)     {   GPIO_InitTypeDef GPIO_InitStructure; \
                                            GPIO_InitStructure.GPIO_Mode        = GPIO_Mode_OUT; \
                                            GPIO_InitStructure.GPIO_PuPd        = GPIO_PuPd_UP; \
                                            GPIO_InitStructure.GPIO_Speed       = GPIO_Speed_50MHz; \
                                            GPIO_InitStructure.GPIO_OType       = GPIO_OType_PP; \
                                            GPIO_InitStructure.GPIO_Pin         = DHT11->gpio_pin; \
                                            GPIO_Init(DHT11->gpio_port,&GPIO_InitStructure);    }

#define __GPIO_CONFIG_IPU(DHT11)        {   GPIO_InitTypeDef GPIO_InitStructure; \
                                            GPIO_InitStructure.GPIO_Mode        = GPIO_Mode_IN; \
                                            GPIO_InitStructure.GPIO_PuPd        = GPIO_PuPd_UP; \
                                            GPIO_InitStructure.GPIO_Speed       = GPIO_Speed_50MHz; \
                                            GPIO_InitStructure.GPIO_OType       = GPIO_OType_PP; \
                                            GPIO_InitStructure.GPIO_Pin         = DHT11->gpio_pin; \
                                            GPIO_Init(DHT11->gpio_port,&GPIO_InitStructure);    }


/* 设置结构体对应的IO口电平状态 */
#define __GPIO_SET_IO(DHT11,Status)    {Status?GPIO_SetBits(DHT11->gpio_port,DHT11->gpio_pin):GPIO_ResetBits(DHT11->gpio_port,DHT11->gpio_pin);}

/* 设置端口高低电平 */
#define __GPIO_IO_H(DHT11)  {__GPIO_SET_IO(DHT11,1)}
#define __GPIO_IO_L(DHT11)  {__GPIO_SET_IO(DHT11,0)}

/* 读取端口高低电平 */
#define __GPIO_READ_IO(DHT11)   ( GPIO_ReadInputDataBit(DHT11->gpio_port,DHT11->gpio_pin) )


/* dht11 初始化函数 */
static void dht11_reset(DHT11_t px_dht)
{
    /* 将引脚置为推挽输出模式 由主机夺取总线控制权 */
    __GPIO_CONFIG_OUT_PP(px_dht);
    
    /* 主机拉低数据总线 告知从机准备开始通信 */
    __GPIO_IO_L(px_dht);
    Delay_ms(20);           //至少拉低18ms
    __GPIO_IO_H(px_dht);
    Delay_us(30);           //主机再次拉高30~40us

    /* DQ总线切换回输入模式，等待从机应答 */
    __GPIO_CONFIG_IPU(px_dht);
}

/* 
*    等待从机应答 ret: 0 !1
*    0应答成功
*    1应答失败 
*/
static int dht11_waitreceive(DHT11_t px_dht)
{
    int wait_time = 0;
    /* 等待从机拉低DQ总线 最长等待时间100us */
    while (__GPIO_READ_IO(px_dht) && wait_time<100)
    {
        wait_time++;
        Delay_us(1);
    }
    /* 如果100us后 从机还未拉低总线 */
    if(wait_time>=100)
        return -1;
    else
        wait_time = 0;
    /* 从机拉低总线后会在100us内再次拉高总线 等待从机拉高总线 */
    while (!__GPIO_READ_IO(px_dht) && wait_time <100)
    {
        wait_time++;
        Delay_us(1);
    }
    /* 如果从机未拉高总线 */
    if(wait_time>=100)
        return -2;
    else
        return 0;
}

/*
*   从机应答后即可开始通信
*   ret: uint8_t
*/
static uint8_t dht11_readbyte(DHT11_t px_dht)
{
    uint8_t data = 0,i;
    uint8_t ret;
    for(i = 0;i<8;i++)
    {
        ret = dht11_waitreceive(px_dht);

        /* 从机给出应答后等待40us即可开始读取总线上的数据 */
        Delay_us(40);   /* wait 40us*/
        
        /* 高位先行 */
        if(__GPIO_READ_IO(px_dht))
        {
            data |= (0x80>>i);
        }
    }
    return data;
}


/* 读取一次温湿度数据，成功返回0，不成功返回1 */
int dht_receive_data(DHT11_t px_dht)
{
    uint8_t rev_data[5],i;
    int ret;
    
    /* 每次读取数据前复位一次dht11 即主机掌握总线控制权 拉高总线后拉低指定时间后再拉高，最后切换回输入模式等待从机应答 */
    dht11_reset(px_dht);

    /* 等待从机响应 */
    ret = dht11_waitreceive(px_dht);
    if(ret == 0)
    {
        for(i = 0;i<5;i++)
        {   
            rev_data[i] = dht11_readbyte(px_dht);
        }
        if((rev_data[0]+rev_data[1]+rev_data[2]+rev_data[3]) == rev_data[4])  /* 校验和 */
        {
            px_dht->humidity = rev_data[0];
            px_dht->temperature = rev_data[2];    
            return 0;
        }
    }
    return 1;
}

                                   
int dht11_init(DHT11_t px_dht,GPIO_TypeDef* GPIOx,uint32_t GPIO_Pin)
{
    /* 初始化时钟配置 */
    if(GPIOx == GPIOA){RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);}
    else if(GPIOx == GPIOB){RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);}
    else if(GPIOx == GPIOC){RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);}
    else if(GPIOx == GPIOD){RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);}
    else if(GPIOx == GPIOE){RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);}
    else if(GPIOx == GPIOF){RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);}
    else if(GPIOx == GPIOG){RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);}
    
    /* 为dht11结构体成员赋值 */
    px_dht->gpio_port = GPIOx;
    px_dht->gpio_pin = GPIO_Pin;
    px_dht->humidity = 0.0;
    px_dht->temperature = 0.0;

    /* 初始化GPIO引脚 */
    __GPIO_INIT(px_dht);
    
    /* 初始引脚置高电平 */
    __GPIO_IO_H(px_dht);
    /* 复位dht11 */
    dht11_reset(px_dht);
    
    return dht11_waitreceive(px_dht);        
}


